/**
 * X-COM Damage Type class
 */
class xT_DamageType extends DamageType
	abstract;

//=============================================================================
// Variables: General
//=============================================================================
/** information on what gibs to spawn and where */
struct sGibInfo
{
	/** the bone to spawn the gib at */
	var name BoneName;
	/** the gib class to spawn */
	var class<xFX_Gib> GibClass;
	var bool bHighDetailOnly;
};

var bool                    bShieldStops; 

var bool					bCausesBlood;				// Whether damage produces blood from victim
var bool					bLocationalHit;				// Whether damage is to a specific location on victim, or generalized.
var bool					bAlwaysGibs;				// Kills with this damage type always blow victim up into small chunks
var bool					bNeverGibs;					// Kills with this damage type *never* blow victim up into small chunks

var bool			            bDirectDamage;
var	bool                        bSeversHead;

var	bool			            bUseTearOffMomentum;	        // For ragdoll death. Add entirety of killing hit's momentum to ragdoll's initial velocity.
var	bool			            bThrowRagdoll;
var bool			            bLeaveBodyEffect;


var	float                       GibPerterbation;				// When gibbing, the chunks will fly off in random directions.
var	int				            GibThreshold;					// Health threshold at which this damagetype gibs

var float                       PhysicsTakeHitMomentumThreshold;    // magnitude of momentum that must be caused by this damagetype for physics based takehit animations to be used on the target

 /** This will delegate to the death effects to the damage type.  This allows us to have specific
 *  damage effects without polluting the Pawn / Weapon class with checking for the damage type
 *
 **/
var	bool                        bUseDamageBasedDeathEffects;

/** Particle system trail to attach to gibs caused by this damage type 
    GibTrail would normally be blood, but we only have robots now. */
var ParticleSystem              GibTrail;

/** whether getting gibbed with this damage type attaches the camera to the head gib */
var bool                        bHeadGibCamera;

/** Whether or not this damage type can cause a blood splatter **/
var bool                        bCausesBloodSplatterDecals;

//=============================================================================
// Variables: DEATH ANIM
//=============================================================================
/** Name of animation to play upon death. */
var(DeathAnim)	name	        DeathAnim;
/** How fast to play the death animation */
var(DeathAnim)	float	        DeathAnimRate;
/** If true, char is stopped and root bone is animated using a bone spring for this type of death. */
var(DeathAnim)	bool	        bAnimateHipsForDeathAnim;
/** If non-zero, motor strength is ramped down over this time (in seconds) */
var(DeathAnim)	float	        MotorDecayTime;
/** If non-zero, stop death anim after this time (in seconds) after stopping taking damage of this type. */
var(DeathAnim)	float	        StopAnimAfterDamageInterval;

//=============================================================================
// Functions: Unused
//=============================================================================
/** Possibly spawn a custom hit effect */
static function SpawnHitEffect(Pawn P, float Damage, vector Momentum, name BoneName, vector HitLocation);

/** DamageType base DoCustomDamageEffects should never be called */
static function DoCustomDamageEffects(x_Pawn ThePawn, class<xT_DamageType> TheDamageType, const out TraceHitInfo HitInfo, vector HitLocation);

/** allows special effects when gibs are spawned via DoCustomDamageEffects() instead of the normal way */
simulated static function SpawnExtraGibEffects(xFX_Gib TheGib );

/** called when a dead pawn viewed by a player was killed by a DamageType with bSpecialDeathCamera set to true */
simulated static function CalcDeathCamera(Pawn P, float DeltaTime, out vector CameraLocation, out rotator CameraRotation, out float CameraFOV);

static function PawnTornOff(Pawn DeadPawn);

//=============================================================================
// Functions: Main
//=============================================================================
/** @return duration of hit effect, primarily used for replication timeout to avoid replicating out of date hits to clients when pawns become relevant */
static function float GetHitEffectDuration(Pawn P, float Damage)
{
	return 0.5;
}

/** allows DamageType to spawn additional effects on gibs (such as flame trails) */
static function SpawnGibEffects(xFX_Gib Gib)
{
	local ParticleSystemComponent Effect;

	if (default.GibTrail != None)
	{
		// we can't use the ParticleSystemComponentPool here as the trails are long lasting/infi so they will not call OnParticleSystemFinished
		Effect = new(Gib) class'UDKParticleSystemComponent';
		Effect.SetTemplate(Default.GibTrail);
		Gib.AttachComponent(Effect);
	}
}

/**
* @param DeadPawn is pawn killed by this damagetype
* @return whether or not we should gib due to damage
*/
static function bool ShouldGib(Pawn DeadPawn)
{
	if (DeadPawn.WorldInfo.IsConsoleBuild(CONSOLE_Mobile))
	{
		return true;
	}
	return ( !Default.bNeverGibs && (Default.bAlwaysGibs || ((DeadPawn.Health < Default.GibThreshold))) );
}

/**
* This will create a skeleton (white boney skeleton) on death.
*
* Currently it doesn't play any Player Death effects as we don't have them yet.
**/
static function CreateDeathSkeleton(xT_Pawn ThePawn, class<xT_DamageType> TheDamageType, const out TraceHitInfo HitInfo, vector HitLocation)
{
	local Array<Attachment> PreviousAttachments;
	local int				Idx;
	local SkeletalMeshComponent PawnMesh;
	local vector Impulse;
	local vector ShotDir;
	local MaterialInstanceTimeVarying MITV_BurnOut;
	local xT_Pawn lThePawn;

	// don't try to make a death skeleton if there is no skel mesh to use
	if (ThePawn.isA('xT_Pawn')) //if not a vehicle
	{
		lThePawn = ThePawn;
		if (lThePawn.DeathMeshSkelMesh == None )
		{
			return;
		}
	
		PawnMesh = lThePawn.Mesh;
		ShotDir = Normal(ThePawn.TearOffMomentum);

		//Mesh.bIgnoreControllers = 1;
		PreviousAttachments = PawnMesh.Attachments;
		lThePawn.SetCollisionSize( 1.0f, 1.0f );
		lThePawn.CylinderComponent.SetTraceBlocking( FALSE, FALSE );

		PawnMesh.SetSkeletalMesh(lThePawn.DeathMeshSkelMesh, TRUE );
		if (lThePawn.DeathMeshPhysAsset != none )
		{
			PawnMesh.SetPhysicsAsset(lThePawn.DeathMeshPhysAsset );
		}

		// set the MITVs for this pawn based off its race
		for( Idx = 0; Idx < lThePawn.SkeletonBurnOutMaterials.Length; ++Idx )  //todo: Materials for burning
		{
			MITV_BurnOut = new(PawnMesh.outer) class'MaterialInstanceTimeVarying';
			MITV_BurnOut.SetParent( lThePawn.default.SkeletonBurnOutMaterials[Idx] );
			// this can have a max of 6 before it wraps and become visible again
			PawnMesh.SetMaterial( Idx, MITV_BurnOut );
			MITV_BurnOut.SetScalarStartTime( 'BurnAmount', 1.0f );
		}

		// Make sure all bodies are unfixed
		if( PawnMesh.PhysicsAssetInstance != none )
		{
			PawnMesh.PhysicsAssetInstance.SetAllBodiesFixed(FALSE);

			// Turn off motors
			PawnMesh.PhysicsAssetInstance.SetAllMotorsAngularPositionDrive(FALSE, FALSE);
		}
		else
		{
			`log( "PawnMesh.PhysicsAssetInstance is NONE!!" );
		}

		for( Idx = 0; Idx < PreviousAttachments.length; ++Idx )
		{
			PawnMesh.AttachComponent( PreviousAttachments[Idx].Component, PreviousAttachments[Idx].BoneName,
						PreviousAttachments[Idx].RelativeLocation, PreviousAttachments[Idx].RelativeRotation,
						PreviousAttachments[Idx].RelativeScale );
		}

		// set all of the materials on the death mesh to be resident
		for( Idx = 0; Idx < lThePawn.DeathMeshSkelMesh.Materials.Length; ++Idx )
		{
			lThePawn.DeathMeshSkelMesh.Materials[Idx].SetForceMipLevelsToBeResident( false, false, 10.0f );
		}


		Impulse = ShotDir * Min( TheDamageType.default.KDamageImpulse, 10 );
		BoneBreaker( lThePawn, PawnMesh, Impulse, HitLocation, HitInfo.BoneName );
	}
}


/**
 * This will look in the NumBonesToPossiblyBreak and choose a bone to break from that list.
 *
 **/
static function BoneBreaker(xT_Pawn ThePawn, SkeletalMeshComponent TheMesh, vector Impulse, vector HitLocation, name BoneName)
{
	local int ConstraintIndex;

	if( BoneName != '' )
	{
		ConstraintIndex = TheMesh.FindConstraintIndex( BoneName );

		if (ConstraintIndex != INDEX_NONE)
		{
			TheMesh.PhysicsAssetInstance.Constraints[ConstraintIndex].TermConstraint();

			// @see PlayDying:  we also do this in the steps after init ragdoll to the full body
			TheMesh.AddImpulse( Impulse, HitLocation, BoneName );
		}
		else
		{
			`log( "was unable to find the Constraint!!!" );
		}
	}
}




//=============================================================================
// DefaultProperties
//=============================================================================
defaultproperties
{
	GibPerterbation = 0.06
	GibThreshold = -50
	GibTrail = ParticleSystem'Envy_Effects.Tests.Effects.P_Vehicle_Damage_1'
	PhysicsTakeHitMomentumThreshold = 250.0
	RadialDamageImpulse = 750
	
	DeathAnimRate = 1.0

	bHeadGibCamera = true
	bAnimateHipsForDeathAnim = true
	bCausesBlood=true
	bCausesBloodSplatterDecals = true
	bLeaveBodyEffect=true
	bUseDamageBasedDeathEffects=true
	bCausesFracture = true
	bArmorStops = true
	bShieldStops = true
}


